/*
 * This file is part of KubeSphere Console.
 * Copyright (C) 2019 The KubeSphere Console Authors.
 *
 * KubeSphere Console is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * KubeSphere Console is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with KubeSphere Console.  If not, see <https://www.gnu.org/licenses/>.
 */

import { action, observable, toJS } from 'mobx'
import { isArray, get, isEmpty, set } from 'lodash'
import { parseUrl, safeParseJSON, getQueryString } from 'utils'
import md5 from 'utils/md5'
import BaseStore from 'stores/devops/base'

export default class SCMStore extends BaseStore {
  constructor(props) {
    super(props)
    this.credentials = window.pipelineCredentials || []
    this.verifyAccessErrorHandle = {
      github: (resp, error) => {
        if (resp.status === 428) {
          this.isAccessTokenWrong = true
          this.orgList.isLoading = false
          return
        }
        if (window.onunhandledrejection) {
          window.onunhandledrejection(error)
        }
      },
      'bitbucket-server': (resp, error) => {
        if (!isEmpty(get(error, 'message'))) {
          // set each field error message
          this.creatBitBucketServersError = safeParseJSON(error.message, {
            errors: [],
          }).errors.reduce((prev, errorItem) => {
            prev[errorItem.field] = errorItem
            return prev
          }, {})
        } else if (resp.status === 428) {
          this.creatBitBucketServersError = {
            password: {
              message: t('username or password wrong, please try again'),
            },
          }
          return
        } else {
          this.creatBitBucketServersError = { all: error.message }
        }
        if (window.onunhandledrejection) {
          window.onunhandledrejection(error)
        }
      },
    }
  }

  @observable
  credentials = {
    isLoading: false,
    data: [],
  }

  @observable
  orgList = {
    isLoading: false,
    data: [],
  }

  @observable
  getRepoListLoading = false

  @observable
  activeRepoIndex = ''
  @observable
  formData = {}
  @observable
  githubCredentialId = ''
  @observable
  creatBitBucketServersError = {}

  @action
  async getOrganizationList(params, scmType) {
    this.orgList.isLoading = true
    this.scmType = scmType
    this.orgParams = params
    const result = await this.request.get(
      `kapis/devops.kubesphere.io/v1alpha2/scms/${scmType ||
        'github'}/organizations/?${getQueryString(params)}`
    )
    isArray(result) ? (this.orgList.data = result) : null
    this.orgList.isLoading = false
  }

  @action
  handleChangeActiveRepoIndex(index) {
    this.activeRepoIndex = index
  }

  @action
  async getRepoList(activeRepoIndex = this.activeRepoIndex) {
    this.getRepoListLoading = true
    const pageNumber =
      get(this.orgList.data[activeRepoIndex], 'repositories.nextPage') || 1
    const organizationName =
      this.orgList.data[activeRepoIndex].key ||
      this.orgList.data[activeRepoIndex].name

    const result = await this.request.get(
      `kapis/devops.kubesphere.io/v1alpha2/scms/${this.scmType ||
        'github'}/organizations/${organizationName}/repositories/?${getQueryString(
        {
          ...this.orgParams,
          pageNumber,
          pageSize: 20,
        }
      )}`
    )
    if (result.repositories) {
      const currentRepository = get(
        this.orgList,
        `data[${activeRepoIndex}].repositories`,
        {}
      )
      // insert new repolist in current orglist
      if (isArray(currentRepository.items)) {
        currentRepository.items = currentRepository.items.concat(
          result.repositories.items
        )
        currentRepository.nextPage = result.repositories.nextPage
      } else {
        set(
          this.orgList,
          `data[${activeRepoIndex}].repositories`,
          result.repositories
        )
      }
      this.getRepoListLoading = false
    }
  }

  async putAccessToken({ token, name }) {
    const result = await this.verifyAccessForRepo({
      accessToken: token,
      scmType: 'github',
    })

    if (result && result.credentialId) {
      this.githubCredentialId = `github-${token.slice(0, 6)}`
      this.createCredential({
        id: this.githubCredentialId,
        username: name,
        password: token,
        description: t('Automatically generated by github'),
        login: 'github',
      })
      this.getOrganizationList({ credentialId: result.credentialId }, 'github')
    }
  }

  async verifyAccessForRepo({ scmType, ...rest }) {
    return await this.request.post(
      `kapis/devops.kubesphere.io/v1alpha2/scms/${scmType}/verify/`,
      rest,
      null,
      this.verifyAccessErrorHandle[scmType]
    )
  }

  async createCredential({ id, username, password, description, login }) {
    return await this.request.post(
      `kapis/devops.kubesphere.io/v1alpha2/devops/${
        this.project_id
      }/credentials`,
      {
        id,
        type: 'username_password',
        username_password: {
          type: 'username_password',
          id,
          username,
          password,
          description,
          login,
        },
      },
      null,
      (resp, res) => {
        if (resp.status === 409) {
          return res
        }
      }
    )
  }

  @action
  getCredential = async project_id => {
    if (project_id) {
      this.project_id = project_id
    }

    this.credentials.isLoading = true
    const result = await this.request
      .get(
        `kapis/devops.kubesphere.io/v1alpha2/devops/${project_id ||
          this.project_id}/credentials`
      )
      .finally(() => {
        this.credentials.isLoading = false
      })
    this.credentials = {
      data: result.map(credential => ({
        label: credential.id,
        value: credential.id,
      })),
      isLoading: false,
    }
    window.pipelineCredentials = toJS(this.credentials) // cache in gloable varibles
  }

  @action
  creatBitBucketServers = async ({ username, password, apiUrl }) => {
    this.creatBitBucketServersError = {}
    this.tokenFormData = { username, password, apiUrl }
    if (isEmpty(parseUrl(apiUrl))) {
      this.creatBitBucketServersError = {
        apiUrl: {
          message: t('url is invalid'),
        },
      }
      return
    }
    this.bitbucketCredentialId = `bitbucket-${username}-${md5(
      apiUrl + username + password
    ).slice(0, 6)}`
    const result = await this.request.post(
      `kapis/devops.kubesphere.io/v1alpha2/scms/bitbucket-server/servers`,
      {
        apiUrl,
        name: this.bitbucketCredentialId,
      },
      null,
      this.verifyAccessErrorHandle['bitbucket-server']
    )
    if (!result || !result.id) {
      return
    }
    const verifyResult = await this.verifyAccessForRepo({
      apiUrl,
      userName: username,
      password,
      scmType: 'bitbucket-server',
    })
    if (verifyResult && verifyResult.credentialId) {
      this.orgList.isLoading = false
      this.getOrganizationList(
        {
          credentialId: `bitbucket-server:${result.id}`,
          apiUrl,
        },
        'bitbucket-server'
      )
      await this.createCredential({
        id: this.bitbucketCredentialId,
        username,
        password,
        description: t(`Automatically generated by bitbucket(${username})`),
      })
    }
  }

  @action
  clearBitbucketErrors = () => {
    this.creatBitBucketServersError = {}
  }

  @action
  resetStore = () => {
    this.orgList = {
      isLoading: false,
      data: [],
    }
    this.activeRepoIndex = ''
    this.githubCredentialId = ''
    this.formData = {}
  }

  @action
  checkCronScript = ({ devops, script, pipeline }) =>
    this.request.post(
      `kapis/devops.kubesphere.io/v1alpha2/devops/${devops}/checkCron`,
      {
        cron: script,
        pipelineName: pipeline,
      }
    )
}
